package com.piece.core.framework.util.collection;

import com.piece.core.framework.util.object.ClassUtil;
import com.piece.core.framework.util.object.ObjectUtil;
import java.lang.reflect.Array;
import java.util.*;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Predicate;

/**
 * 数组工具
 */
public class ArrayUtil {

    /**
     * 判断数组是否相等
     *
     * @param sources 原始数据
     * @param targets 比较数据
     */
    public static final boolean equals(byte[] sources, byte[] targets) {
        if (sources == targets) {
            return true;
        }
        if (null == sources || null == targets) {
            return false;
        }
        final int length = sources.length;
        if (length != targets.length) {
            return false;
        }
        for (int index = 0; index < length; index++) {
            if (sources[index] != targets[index]) {
                return false;
            }
        }
        return true;
    }

    /**
     * 比较数组大小（无符号比较）
     * <p>长度不同时：长度长的数组大</p>
     * <p>长度相同时：高位字符（索引小）大的数组大</p>
     *
     * @param sources 原始数据
     * @param targets 比较数据
     */
    public static final int compareUnsigned(byte[] sources, byte[] targets) {
        if (null == sources || null == targets) {
            throw new RuntimeException("数组比较参数错误");
        } else if (sources.length != targets.length) {
            return sources.length > targets.length ? 1 : -1;
        } else {
            final int length = sources.length;
            for (int index = 0; index < length; index++) {
                if (sources[index] != targets[index]) {
                    return ((char) (sources[index] & 0xFF)) > ((char) (targets[index] & 0xFF)) ? 1 : -1;
                }
            }
            return 0;
        }
    }

    /**
     * 异或运算
     *
     * @param sources 原始数据
     * @param targets 比较数据
     */
    public static final byte[] xor(byte[] sources, byte[] targets) {
        if (null == sources || null == targets) {
            throw new RuntimeException("异或运算参数错误");
        } else if (sources.length != targets.length) {
            throw new RuntimeException("异或运算参数错误（长度）");
        } else {
            final int length = sources.length;
            final byte[] result = new byte[length];
            for (int index = 0; index < length; index++) {
                result[index] = (byte) (sources[index] ^ targets[index]);
            }
            return result;
        }
    }

    /**
     * 差异索引
     * <p>差异索引越小：差距越大</p>
     * <p>差异索引越大：差距越小</p>
     *
     * @param sources 原始数据
     * @param targets 比较数据
     */
    public static final int diffIndex(byte[] sources, byte[] targets) {
        if (null == sources || null == targets) {
            throw new RuntimeException("差异索引参数错误");
        } else if (sources.length != targets.length) {
            throw new RuntimeException("差异索引参数错误（长度）");
        } else {
            final int length = sources.length;
            for (int index = 0; index < length; index++) {
                if (sources[index] != targets[index]) {
                    return index;
                }
            }
            return length;
        }
    }

    /**
     * 数组是否为空
     */
    public static final boolean isEmpty(Object[] objects) {
        return null == objects || objects.length == 0;
    }

    /**
     * 数组是否非空
     */
    public static final boolean isNotEmpty(Object[] objects) {
        return !isEmpty(objects);
    }

    /**
     * 字节数组是否为空
     */
    public static final boolean isEmpty(byte[] bytes) {
        return null == bytes || bytes.length == 0;
    }

    /**
     * 字节数组是否非空
     */
    public static final boolean isNotEmpty(byte[] bytes) {
        return !isEmpty(bytes);
    }

    public static boolean isEmpty(char[] array) {
        return null == array || array.length == 0;
    }

    public static boolean isNotEmpty(char[] array) {
        return !isEmpty(array);
    }

    public static final boolean isEmpty(Collection<?> collection) {
        return null == collection || collection.isEmpty();
    }

    public static final boolean isNotEmpty(Collection<?> collection) {
        return !isEmpty(collection);
    }

    public static final boolean isEmpty(Iterable<?> iterable) {
        return null == iterable || isEmpty(iterable.iterator());
    }

    public static final boolean isEmpty(Iterator<?> iterator) {
        return null == iterator || !iterator.hasNext();
    }

    public static boolean isArray(Object obj) {
        return null != obj && (obj.getClass().isArray() || obj instanceof Collection);
    }

    /**
     * 判断是否包含数据
     */
    public static boolean contains(int[] array, char value) {
        return indexOf(array, value) > -1;
    }

    /**
     * 查找数组索引
     *
     * @param values 数组
     * @param value  查找数值
     */
    public static int indexOf(int[] values, int value) {
        return indexOf(values, 0, values.length, value);
    }

    /**
     * 查找数组索引
     *
     * @param values 数组
     * @param begin  开始位置
     * @param end    结束位置
     * @param value  查找数值
     */
    public static int indexOf(int[] values, int begin, int end, int value) {
        for (int index = begin; index < end; index++) {
            if (values[index] == value) {
                return index;
            }
        }
        return -1;
    }

    /**
     * 查找最后匹配到的位置
     */
    public static int lastIndexOf(int[] array, char value) {
        if (null != array) {
            for(int i = array.length - 1; i >= 0; --i) {
                if (value == array[i]) {
                    return i;
                }
            }
        }
        return -1;
    }

    /**
     * 判断是否包含数据
     */
    public static boolean contains(char[] array, char value) {
        return indexOf(array, value) > -1;
    }

    /**
     * 查找数组索引
     *
     * @param chars 数组
     * @param value 查找数值
     */
    public static int indexOf(char[] chars, char value) {
        for (int index = 0; index < chars.length; index++) {
            if (value == chars[index]) {
                return index;
            }
        }
        return -1;
    }

    /**
     * 查找最后匹配到的位置
     */
    public static int lastIndexOf(char[] array, char value) {
        if (null != array) {
            for(int i = array.length - 1; i >= 0; --i) {
                if (value == array[i]) {
                    return i;
                }
            }
        }
        return -1;
    }

    /**
     * 过滤重复数据
     */
    public static <T> ArrayList<T> distinct(Collection<T> collection) {
        if (isEmpty(collection)) {
            return new ArrayList();
        } else {
            return collection instanceof Set ? new ArrayList(collection) : new ArrayList(new LinkedHashSet(collection));
        }
    }

    public static int length(Object array) throws IllegalArgumentException {
        return null == array ? 0 : Array.getLength(array);
    }

    public static <T> T get(Object array, int index) {
        if (null == array) {
            return null;
        } else {
            if (index < 0) {
                index += Array.getLength(array);
            }

            try {
                return (T) Array.get(array, index);
            } catch (ArrayIndexOutOfBoundsException var3) {
                return null;
            }
        }
    }

    public static <T> T[] getAny(Object array, int... indexes) {
        if (null == array) {
            return null;
        } else {
            T[] result = (T[]) Array.newInstance(array.getClass().getComponentType(), indexes.length);
            int[] var3 = indexes;
            int var4 = indexes.length;

            for(int var5 = 0; var5 < var4; ++var5) {
                int i = var3[var5];
                result[i] = get(array, i);
            }

            return result;
        }
    }

    public static <T> T[] sub(T[] array, int start, int end) {
        int length = length(array);
        if (start < 0) {
            start += length;
        }

        if (end < 0) {
            end += length;
        }

        if (start == length) {
            return (T[]) Array.newInstance(array.getClass().getComponentType(), 0);
        } else {
            if (start > end) {
                int tmp = start;
                start = end;
                end = tmp;
            }

            if (end > length) {
                if (start >= length) {
                    return (T[]) Array.newInstance(array.getClass().getComponentType(), 0);
                }

                end = length;
            }

            return Arrays.copyOfRange(array, start, end);
        }
    }

    public static Object[] sub(Object array, int start, int end) {
        return sub(array, start, end, 1);
    }

    public static Object[] sub(Object array, int start, int end, int step) {
        int length = length(array);
        if (start < 0) {
            start += length;
        }

        if (end < 0) {
            end += length;
        }

        if (start == length) {
            return new Object[0];
        } else {
            if (start > end) {
                int tmp = start;
                start = end;
                end = tmp;
            }

            if (end > length) {
                if (start >= length) {
                    return new Object[0];
                }

                end = length;
            }

            if (step <= 1) {
                step = 1;
            }

            ArrayList<Object> list = new ArrayList();

            for(int i = start; i < end; i += step) {
                list.add(get(array, i));
            }

            return list.toArray();
        }
    }

    public static String join(Object[] array, String delimiter) {
        return join(array, delimiter, "", "");
    }

    public static String join(Object[] array, String delimiter, String prefix, String suffix) {
        if (null == array) {
            return null;
        }
        return join(Arrays.stream(array).iterator(), delimiter, prefix, suffix);
    }

    public static String join(Iterable iterable, String delimiter) {
        return join(iterable, delimiter, null, null);
    }

    public static String join(Iterable iterable, String delimiter, String prefix, String suffix) {
        StringBuffer result = new StringBuffer();
        if (null != iterable) {
            Iterator it = iterable.iterator();
            return join(it, delimiter, prefix, suffix);
        }
        return result.toString();
    }

    public static String join(Iterator iterator, String delimiter) {
        return join(iterator, delimiter, null, null);
    }

    public static String join(Iterator iterator, String delimiter, String prefix, String suffix) {
        StringBuffer result = new StringBuffer();
        if (null != iterator) {
            while (iterator.hasNext()) {
                result.append(delimiter);
                if(null != prefix) {
                    result.append(prefix);
                }
                result.append(iterator.next().toString());
                if(null != suffix) {
                    result.append(suffix);
                }
            }
            if (result.length() > 0) {
                return result.substring(delimiter.length());
            }
        }
        return result.toString();
    }

    public static <T> T[] insert(T[] buffer, int index, T... newElements) {
        return (T[])insert((Object)buffer, index, newElements);
    }

    public static <T> Object insert(Object array, int index, T... newElements) {
        if (isEmpty(newElements)) {
            return array;
        } else if (ObjectUtil.isEmpty(array)) {
            return newElements;
        } else {
            int len = length(array);
            if (index < 0) {
                index = index % len + len;
            }

            T[] result = (T[]) Array.newInstance(array.getClass().getComponentType(), Math.max(len, index) + newElements.length);
            System.arraycopy(array, 0, result, 0, Math.min(len, index));
            System.arraycopy(newElements, 0, result, index, newElements.length);
            if (index < len) {
                System.arraycopy(array, index, result, index + newElements.length, len - index);
            }

            return result;
        }
    }

    public static <T> T[] append(T[] buffer, T... newElements) {
        return isEmpty(buffer) ? newElements : insert(buffer, buffer.length, newElements);
    }

    public static <T> Object append(Object array, T... newElements) {
        return ObjectUtil.isEmpty(array) ? newElements : insert(array, length(array), newElements);
    }

    public static <T> T[] setOrAppend(T[] buffer, int index, T value) {
        if (index < buffer.length) {
            Array.set(buffer, index, value);
            return buffer;
        } else {
            return append(buffer, value);
        }
    }

    public static Object setOrAppend(Object array, int index, Object value) {
        if (null == array) {
            return null;
        }

        if (index < length(array)) {
            Array.set(array, index, value);
            return array;
        } else {
            return append(array, value);
        }
    }

    /**
     * 将 object 和 newElements 合并成一个数组
     */
    @SafeVarargs
    public static <T> Consumer<T>[] append(Consumer<T> object, Consumer<T>... newElements) {
        if (null == object) {
            return newElements;
        }
        Consumer<T>[] result = (Consumer<T>[]) Array.newInstance(Consumer.class, 1 + newElements.length);
        result[0] = object;
        System.arraycopy(newElements, 0, result, 1, newElements.length);
        return result;
    }

    /**
     * 转换成数组对象
     */
    public static <T> T[] convertArray(Collection<T> from) {
        if (isEmpty(from)) {
            return (T[]) (new Object[0]);
        }

        Class type = ClassUtil.getComponentType(from.iterator());
        Object instance = Array.newInstance(type, 0);
        return from.toArray((T[])instance);
    }

    /**
     * 按条件转换成数据对象
     */
    public static <T, R> R[] convertArray(Object array, Class<R> targetComponentType, Function<? super T, ? extends R> func) {
        int length = length(array);
        R[] result = (R[]) Array.newInstance(targetComponentType, length);

        for(int i = 0; i < length; ++i) {
            result[i] = func.apply(get(array, i));
        }

        return result;
    }

    /**
     * 返回第一个匹配成功的对象
     */
    public static <T> T firstMatch(Predicate<T> predicate, T... array) {
        if (isNotEmpty(array)) {
            Object[] var2 = array;
            int var3 = array.length;

            for(int var4 = 0; var4 < var3; ++var4) {
                T val = (T) var2[var4];
                if (predicate.test(val)) {
                    return val;
                }
            }
        }

        return null;
    }
}
